#ifndef DRV_NET_H
#define DRV_NET_H
#include <stdint.h>
#include "FreeRTOS.h"
#include "queue.h"
#include "semphr.h"

#include "at_lexer.h"
#include "drv_net.h"
#include "app_fifo.h"
#include <stdbool.h>

#define NET_LOG_ENABLE 1
#define NET_LOG_LEVEL NET_LOG_LEVEL_DBG

#define NET_LOG_LEVEL_NONE  0
#define NET_LOG_LEVEL_ERR   1
#define NET_LOG_LEVEL_WAR   2
#define NET_LOG_LEVEL_INF   3
#define NET_LOG_LEVEL_DBG   4

#if NET_LOG_ENABLE && NET_LOG_LEVEL >= NET_LOG_LEVEL_ERR
    #define NET_ERR(__format,...) SEGGER_RTT_printf(0,"ERR:%s@%d:"__format"\r\n",__FUNCTION__,__LINE__,##__VA_ARGS__)
#else
    #define NET_ERR(__format,...)
#endif

#if NET_LOG_ENABLE && NET_LOG_LEVEL >= NET_LOG_LEVEL_WAR
    #define NET_WAR(__format,...) SEGGER_RTT_printf(0,"WAR:%s@%d:"__format"\r\n",__FUNCTION__,__LINE__,##__VA_ARGS__)
#else
    #define NET_WAR(__format,...)
#endif


#if NET_LOG_ENABLE && NET_LOG_LEVEL >= NET_LOG_LEVEL_INF
    #define NET_INF(__format,...) SEGGER_RTT_printf(0,"INF:%s@%d:"__format"\r\n",__FUNCTION__,__LINE__,##__VA_ARGS__)
#else
    #define NET_INF(__format,...)
#endif

#if NET_LOG_ENABLE && NET_LOG_LEVEL >= NET_LOG_LEVEL_DBG
    #define NET_DBG(__format,...) SEGGER_RTT_printf(0,"DBG:%s@%d:"__format"\r\n",__FUNCTION__,__LINE__,##__VA_ARGS__)
    #define NET_PUTCHAR(_char_) SEGGER_RTT_PutChar(0,_char_);
#else
    #define NET_DBG(__format,...)
    #define NET_PUTCHAR(_char_)
#endif

typedef struct 
{
	uint32_t  year;
	uint32_t  month;
	uint32_t  days;
	uint32_t  hours;
	uint32_t  min;
	uint32_t  sec;
} NET_DATE_T;

typedef enum {
    AT_RESULT_OK                = 0x00010001,
    AT_RESULT_ERROR             = 0x00010002,
}AT_RESULT;

typedef enum {
    AT_INFO_CONNECT_OK        = 0x00010003,
    AT_INFO_UPDATE_OK         = 0x00010004,
    AT_INFO_CLOSE_OK          = 0x00010005,
    AT_INFO_LWOBSERVE_REQUEST = 0x00010006,
    AT_INFO_LWOBSERVE_CANCEL  = 0x00010007,
    AT_INFO_CARD_READY        = 0x00010008,
    AT_INFO_CARD_ERR          = 0x00010009,
    AT_INFO_CGATT0            = 0x0001000A,
    AT_INFO_CGATT1            = 0x0001000B,
    AT_INFO_IP                = 0x0001000C,
    AT_INFO_CGPADDR           = 0x0001000D,
    AT_INFO_CSI_OK            = 0x0001000E,
    AT_INFO_CSI_ERROR         = 0x0001000F,
    AT_INFO_REG_SUCCESS       = 0x00010010,
    AT_INFO_REG_FAILED        = 0x00010011,
    AT_INFO_REG_TIMEOUT       = 0x00010012,
    AT_INFO_MIPLDISCOVER_OK   = 0x00010013,
    AT_INFO_MIPLDISCOVER_ERROR = 0x00010014,
    AT_INFO_DEREG_DONE        = 0x00010015,
    AT_INFO_SOCKET_OPEN_OK    = 0x00010016,
    AT_INFO_SOCKET_OPEN_ERROR = 0x00010017,
    AT_INFO_SOCKET_SEND_OK    = 0x00010018,
    AT_INFO_SOCKET_SEND_FAIL  = 0x00010019,
    AT_INFO_CEREG_OK          = 0x0001001A,
    AT_INFO_SOCKET_CLOSE_OK   = 0x0001001B,
    AT_INFO_CSQ               = 0x0001001C,
    AT_INFO_SOCKET_ALLOC      = 0x0001001D,
    AT_INFO_DISCOVER          = 0x0001001E,
    AT_INFO_CSCON             = 0x0001001F,
    AT_INFO_NPSMR             = 0x00010020,
    AT_INFO_CGMM              = 0x00010021,
    AT_INFO_CEREG             = 0x00010022,
    AT_INFO_SOCKET_CONNECT    = 0x00010023,
    AT_INFO_SOCKET_SEND       = 0x00010024,
    AT_INFO_ONENET_EVENT      = 0x00010025,
    AT_INFO_CPIN              = 0x00010026,
    AT_INFO_COPS              = 0x00010027,
    AT_INFO_MIPOPEN           = 0x00010028,
    AT_INFO_MIPSEND           = 0x00010029,
    AT_INFO_MIPCLOSE          = 0x0001002A,
    AT_INFO_CREG              = 0x0001002B,
    AT_INFO_CGATT             = 0x0001002C
}AT_INFO;


typedef struct _SOCKET_INF {
    int16_t id;
    int16_t state;
}SOCKET_INF;

#pragma anon_unions
typedef struct _MSG_AT {
    union {
        uint32_t pad;
        AT_RESULT result;
        AT_INFO info;
    };
    union {
        int32_t value;
        SOCKET_INF socket_inf;
    };
}MSG_AT;


typedef enum {
    NET_ERR_NO_CARD = -5,
    NET_ERR_UART = -4,
    NET_ERR_STATE = -3,
    NET_ERR_UNKOWN = -2,
    NET_ERR_AT = -1,
    NET_ERR_OK = 0,
}NET_ERR_CODE;


typedef enum {
    NET_STATE_OFF,
    NET_STATE_SLEEP,
    NET_STATE_READY,
    NET_STATE_MAX
}NET_STATE;



typedef enum {
    NET_POWER_OFF = 0,
    NET_POWER_ON,
    NET_POWER_SLEEP,
    NET_POWER_WAKE
}NET_POWER_CONTROL;

typedef enum {
    DRV_NET_SOCKET_TCP,
    DRV_NET_SOCKET_UDP
}DRV_NET_SOCKET_TYPE;

typedef enum {
    MSG_RECV_SOCKET,
    MSG_RECV_OBSERVE,
    MSG_RECV_DISCOVER,
    MSG_RECV_WRITE,
    MSG_RECV_READ,
	MSG_RECV_UPGRADE,
}MSG_RECV_TYPE;


typedef struct {
    MSG_RECV_TYPE type;
    uint32_t value;
}MSG_RECV;


#define CM_OBJ_ID           3348
#define CM_RES_ID           5750
#define CM_RES_ID_STR       "5750"
#define CM_INS_ID_UP        0
#define CM_INS_ID_DOWN      1



#define DRV_NET_MAX_NUM_OF_CONNECTIONS 3


//typedef enum{
//    SERVICE_UDP,
//    SERVICE_TCP
//}SERVICE_TYPE;
typedef enum {
    DRV_NET_SERVICE_CM,     /**< China Mobile IOT platform:OneNET */
    DRV_NET_SERVICE_CT,     /**< China TELE IOT platform:Ocean Connect */
    DRV_NET_SERVICE_TCP,    /**< Private server:TCP */
    DRV_NET_SERVICE_UDP     /**< Private server:UDP */
}DRV_NET_SERVICE;

typedef enum {
    SERVICE_CLOSED,
    SRRVICE_OPEN
}SREVICE_STATE;

typedef void (*DRV_NET_RECV_HANDLER)(uint8_t data);

typedef struct _net_connection {
    app_fifo_t fifo;
    DRV_NET_SERVICE type;
    SREVICE_STATE state;
    DRV_NET_RECV_HANDLER handler;
    int32_t socket;
    char paddr[32];
    char pport[8];
}NET_CONNECTION;


typedef enum {
    DRV_NET_DEVICE_BC26 = 0,
    DRV_NET_DEVICE_FT780,
    DRV_NET_DEVICE_M5310A,
    DRV_NET_DEVICE_M5313,
    DRV_NET_DEVICE_A9600,
    DRV_NET_DEVICE_S800C,
    DRV_NET_DEVICE_MAX      /**< The device whose number is greater than or equal to DRV_NET_DEVICE_MAX 
                                 is invalid  */
}DRV_NET_DEVICE;

typedef enum {
    DRV_NET_CARRIER_CM,     /**< China Mobile */
    DRV_NET_CARRIER_CT,     /**< China Tele */
	DRV_NET_CARRIER_CU,     /**< China Unicom */
    DRV_NET_CARRIER_MAX     /**< Invalid carrier */
}DRV_NET_CARRIER;

typedef enum {
    NET_CMD_SQ_GET = 0,
    NET_CMD_CT_ID_TYPE_GET = 1,
    NET_CMD_CELL_INF_GET = 2,
	NET_CMD_CCLK_TIME_GET = 3,
    NET_CMD_MAX
}NET_CMD_CODE;

typedef enum {
    NET_CT_ID_IMEI = 0,
    NET_CT_ID_IMSI = 1,
    NET_CT_ID_MAX  = 2
}NET_CT_ID_TYPE;

typedef struct _DRV_NET_INTERFACE {
    //base interface
    NET_ERR_CODE (*pf_power_set)(NET_POWER_CONTROL);
    NET_ERR_CODE (*pf_executor)(NET_CMD_CODE, void *);
    //ct interface
    NET_ERR_CODE (*pf_ct_connect)(char *, char *, uint32_t);
    NET_ERR_CODE (*pf_ct_send)(uint8_t *, uint32_t);
    NET_ERR_CODE (*pf_ct_disconnect)(void);
    //cm interface
    NET_ERR_CODE (*pf_cm_connect)(char *, char *, uint32_t);
    NET_ERR_CODE (*pf_cm_send)(uint8_t *, uint32_t);
    NET_ERR_CODE (*pf_cm_disconnect)(void);
    //socket interface
    NET_ERR_CODE (*pf_socket_connect)(int32_t, DRV_NET_SOCKET_TYPE, char *, char *, uint32_t);
    NET_ERR_CODE (*pf_socket_send)(int32_t, uint8_t *, uint32_t);
    NET_ERR_CODE (*pf_socket_disconnect)(int32_t);
}DRV_NET_INTERFACE;

typedef struct _CELL_INF {
    uint32_t pci;
    char cell_id[8];
}CELL_INF;

typedef struct _ENV_NET {
    DRV_NET_DEVICE device;
    DRV_NET_CARRIER carrier;
    DRV_NET_SERVICE service;
    uint32_t fifo_size;
    char *pserver_ip;
    char *pserver_port;
    volatile NET_STATE state;
    uint32_t cid;
    uint32_t sq;
    CELL_INF cell_inf;
	NET_DATE_T date_t;
//    uint32_t csi;//current communication suite instance
//    uint32_t cm_obj_id;
    uint32_t cm_up_msg_id;
    uint32_t cm_down_msg_id;
    QueueHandle_t qresult;
    QueueHandle_t qinfo;
    QueueHandle_t qrecv;
    SemaphoreHandle_t sem_at; //mutex for at command
    SemaphoreHandle_t sem_send; //mutex
    AT_HANDLER *pat_handlers;
    char pimei[32];
    char pimsi[32];
	char qccid[32];
    char pip[64]; //might be ipv6
	char nb_version[20];
    NET_CONNECTION pconns[DRV_NET_MAX_NUM_OF_CONNECTIONS];
    const DRV_NET_INTERFACE *pif_devices[DRV_NET_DEVICE_MAX];
	uint8_t sim_status;
}ENV_NET;

extern ENV_NET env_net;
extern void drv_net_power_wake_up(void);
extern void drv_net_power_on(void);
extern void drv_net_power_off(void);

/**
 * @brief Initialize the net module
 * 
 * @author Tang (2019/1/4)
 * 
 * @param void 
 * 
 * @retval 0 OK 
 * @retval others Error
 */
extern int32_t drv_net_init(void);

/**
 * @brief The task is used to receive and parse the net module's acknowledgement  
 * 
 * @author Tang (2019/1/4)
 * 
 * @param parg Pointer to the environment of task 
 *  
 * @note You must ensure the priority of this task is highest 
 */
extern void drv_net_task_uart_receive(void *parg);

/**
 * @brief The task is used to receive and acknowledge the data from TCP/UDP or Platform
 * 
 * @author Tang (2019/1/4)
 * 
 * @param parg Pointer to the environment of task 
 *  
 * @note You must ensure the priority of this task is lower than the uart receive task's 
 */
extern void drv_net_task_msg_recv(void *parg);

/**
 * @brief configure the nb modules
 * 
 * @author Tang (2018/12/3)
 * 
 * @param[in] service OneNet,Ocean Connect,TCP or UDP,@ref DRV_NET_SERVICE
 * @param[in] pserver_ip Pointer to the string of server ip
 * @param[in] pserver_port Pointer to the string of server port
 * @param[in] fifo_size Receive buffer size
 * 
 * @return NET_ERR_CODE @ref NET_ERR_CODE
 *  
 */
extern NET_ERR_CODE drv_net_configure(DRV_NET_SERVICE service,
                                      char *pserver_ip,
                                      char *pserver_port,
                                      uint32_t fifo_size);

/**
 * @brief Control the module power
 * 
 * @author Tang (2018/12/3)
 * 
 * @param[in] ctrl @ref NET_POWER_CONTROL
 * 
 * @return NET_ERR_CODE @ref NET_ERR_CODE
 */
extern NET_ERR_CODE drv_net_power_set(NET_POWER_CONTROL ctrl);

/**
 * @brief Read current state of net module
 * 
 * @author Tang (2019/1/3)
 * 
 * @param void 
 * 
 * @return NET_STATE @ref NET_STATE
 */
extern NET_STATE drv_net_state_get(void);

/**
 * @brief Get the signal quality of net module
 * 
 * @author Tang (2019/1/3)
 * 
 * @param[out] psq Pointer to the sq
 * 
 * @return NET_ERR_CODE @ref NET_ERR_CODE
 */
extern NET_ERR_CODE drv_net_sq_get(uint32_t *psq);

/**
 * @brief Read the net module's IMEI
 * 
 * @author Tang (2019/1/4)
 * 
 * @param void 
 * 
 * @return char* Pointer to the string of IMEI
 */
extern char* drv_net_imei_get(void);

/**
 * @brief Read the net module's IMSI
 * 
 * @author Tang (2019/1/4)
 * 
 * @param void 
 * 
 * @return char* Pointer to the string of IMSI
 */
extern char* drv_net_imsi_get(void);

/**
 * @brief Read the device type of net module
 * 
 * @author Tang (2019/1/4)
 * 
 * @param void 
 * 
 * @return DRV_NET_DEVICE @ref DRV_NET_DEVICE
 */
extern DRV_NET_DEVICE drv_net_device_get(void);

/**
 * @brief Predicate whether the module supports cm
 * 
 * @author Tang (2019/2/18)
 * 
 * @param void 
 * 
 * @return bool 
 */
bool drv_net_support_cm(void);


/**
 * @brief Predicate whether the module supports ct
 * 
 * @author Tang (2019/2/18)
 * 
 * @param void 
 * 
 * @return bool 
 */
bool drv_net_support_ct(void);

/**
 * @brief Read which sn is used by ct
 * 
 * @author Tang (2019/2/18)
 * 
 * @param void 
 * 
 * @return NET_CT_ID_TYPE @ref NET_CT_ID_TYPE
 */
extern NET_CT_ID_TYPE drv_net_ct_id_type(void);
/**
 * @brief Read current cell inf
 * 
 * @author Tang (2019/5/14)
 * 
 * @param void 
 * 
 * @return NET_ERR_CODE 
 */
extern NET_ERR_CODE drv_net_cell_inf_get(CELL_INF *pcell_inf);
/**
 * @brief Connect to the platform or server by the configuration
 * 
 * @author Tang (2018/12/3)
 * 
 * @return NET_ERR_CODE @ref NET_ERR_CODE
 */
extern NET_ERR_CODE drv_net_connect(void);

/**
 * @brief Send data to platform or server
 * 
 * @author Tang (2018/12/3)
 * 
 * @param[in] pdata Pointer to the data
 * @param[in] len Length of the data
 * 
 * @return NET_ERR_CODE @ref NET_ERR_CODE
 */
extern NET_ERR_CODE drv_net_send(uint8_t *pdata, uint32_t len);

/**
 * @brief Disconnect from the platform or server
 * 
 * @author Tang (2018/12/3)
 * 
 * @return NET_ERR_CODE 
 */
extern NET_ERR_CODE drv_net_disconnect(void);

/**
 * @brief Open a TCP/UDP connection
 * 
 * @author Tang (2018/12/3)
 * 
 * @param[in] socket (0,DRV_NET_MAX_NUM_OF_CONNECTIONS)
 * @param[in] service_type TCP/UDP,@ref DRV_NET_SOCKET_TYPE
 * @param[in] pstr_ip Desination's address
 * @param[in] pstr_port Destination's port
 * @param[in] fifo_size Receive buffer size
 * 
 * @return NET_ERR_CODE @ref NET_ERR_CODE
 */
extern NET_ERR_CODE drv_net_socket_connect(int32_t socket,
                                           DRV_NET_SOCKET_TYPE service_type,
                                           char *pstr_ip,
                                           char *pstr_port,
                                           uint32_t fifo_size);

/**
 * @brief Send data by TCP/UDP
 * 
 * @author Tang (2018/12/3)
 * 
 * @param[in] socket socket id
 * @param[in] pdata Pointer to the data
 * @param[in] len Length of the data
 * 
 * @return NET_ERR_CODE @ref NET_ERR_CODE
 */
extern NET_ERR_CODE drv_net_socket_send(int32_t socket, uint8_t *pdata, uint32_t len);

/**
 * @brief Close the TCP/UDP connection 
 * 
 * @author Tang (2018/12/3)
 * 
 * @param socket Socket id
 * 
 * @return NET_ERR_CODE @ref NET_ERR_CODE
 */
extern NET_ERR_CODE drv_net_socket_disconnect(int32_t socket);

/**
 * @brief Set the receiving handler
 * 
 * @author Tang (2018/12/3)
 * 
 * @param[in] socket Socket id
 * @param[in] pf_recv_handler Function pointer to the receiving handler
 * 
 * @return NET_ERR_CODE @ref NET_ERR_CODE
 */
extern NET_ERR_CODE drv_net_recv_handler_set(int32_t socket, void (*pf_recv_handler)(uint8_t data));

/**
 * @brief Install the uart and gpios of net module 
 * 
 * @author Tang (2018/12/21)
 * 
 * @param void 
 * 
 * @return int32_t OK(0)/ERROR(others)
 */
extern int32_t drv_net_install(void);

/**
 * @brief Uninstall the uart and gpios of net module
 * 
 * @author Tang (2018/12/21)
 * 
 * @param void 
 * 
 * @return int32_t 
 */
extern int32_t drv_net_uninstall(void);

/**
 * @brief get current net time
 * 
 * @author zhang (2019/5/22)
 * 
 * @param[in] NET_DATE_T *t
 * 
 * @return NET_ERR_CODE 
 */
extern NET_ERR_CODE drv_net_date_info_get(NET_DATE_T *t);

/**
 * Find carrier through the prefix of imsi
 * 
 * @author Tang (2019/1/28)
 * 
 * @param pimsi Pointer to the imsi
 * 
 * @return DRV_NET_CARRIER @ref DRV_NET_CARRIER
 */
extern DRV_NET_CARRIER drv_net_find_carrier_by_imsi(const char *pimsi); 

NET_ERR_CODE drv_net_msg_at_receive(AT_INFO at_info,
                                    uint32_t n_times,
                                    uint32_t interval,
                                    MSG_AT *pmsg_at,
                                    const char *pinf);

extern void drv_net_task_test(void *parg);
extern NET_ERR_CODE drv_net_fifo_init(app_fifo_t *papp_fifo, uint32_t fifo_size);

NET_ERR_CODE drv_net_msg_at_recv(MSG_AT *pmsg_at, uint32_t times, uint32_t inteval, uint32_t num, ...);

void rtt_init(void);
void rtt_recv_task(void *parg);
void rtt_test_task(void *parg);



// date -> sec  return sec
uint32_t drv_net_date_to_sec(NET_DATE_T t, uint32_t* total_sec) ;

// correct time +- h
void drv_net_date_correct(NET_DATE_T* t, int32_t h) ; 

extern void drv_net_result_ok(uint8_t *pdata, uint32_t len);
extern void drv_net_result_error(uint8_t *pdata, uint32_t len);
char* drv_net_qccid_get(void);

extern NET_ERR_CODE drv_net_write_bytes_raw(uint32_t timeout, uint32_t retry, uint8_t *pdata, uint32_t len);

#endif


